iT邦幫忙

2024 iThome 鐵人賽

DAY 4
0
生成式 AI

Python 新手的 AI 之旅:從零開始打造屬於你的 AI / LLM 應用系列 第 4

【Day4】提示詞工程 (Prompt Engineering) 這什麼鬼:初探 LLM 操控術與 OpenAI 的六大策略

  • 分享至 

  • xImage
  •  

前天 (Day4) 我們嘗試向模型輸入一些文字,然後也發現了一些問題:如果使用 transformer 套件來呼叫本地模型,它只會做文字接龍,那 ChatGPT 是怎麼做到對話的呢?

Prompt 是什麼?Prompt Engineering 是什麼?

我們來看一下維基百科寫了什麼

A prompt is natural language text describing the task that an AI should perform
(提示詞就是用自然語言描述給 AI 要執行任務的文本)

以 Day2 的範例來說,「I love you, but」就是一個 prompt;Day3 的範例「你好ㄚ可愛的模型」也是一個 prompt。

一樣看看維基百科對提示詞工程 (Prompt Engineering) 的定義

Prompt engineering is the process of structuring an instruction that can be interpreted and understood by a generative AI model
(提示詞工程是建構指令的過程,目標是讓生成式 AI 能夠理解和解讀該指令)

我們的目標是讓機器來幫人類解決問題,因此讓機器讀懂指令就十分重要。從高階程式碼轉換成 CPU 能讀懂的指令集;到現在使用自然語言描述抽象任務給 AI。目標都是用清楚的指令告訴機器,讓他完成我們的需求。這個過程就叫做「提示詞工程 (Prompt Engineering)」

image (1).webp

(圖片由 black-forest-labs/FLUX.1-schnell 生成)

用有角色的 Prompt 來模擬對話

回到開頭的問題,既然知道大型語言模型只會文字接龍,那我們就把對話的角色也加入到 prompt 就行了呀!就好像小說的角色在對話一樣

我們把 Day2 的 prompt 加上「角色」讓模型知道接下來要接下來的是「回答另一個角色」的內容,看看 GPT2 會怎麼回我吧!(注意:”\n” 是換行符號的意思)

"I love you, but" 改成 "User: I love you.\nAssistant:" 看看結果如何

image1.png

再來用一樣的模板,問它什麼是 AI 吧!"User: What is AI?\nAssistant:"

image2.png

System 系統參數

我們在使用 OpenAI, Groq API 時,常常會在官方文件看到這樣的 Prompt 範例

messages: [
    { role: "system", content: "You are a helpful assistant." },
    {
        role: "user",
        content: "Write a haiku about recursion in programming.",
    },
],

有了角色 prompt 的經驗後,你應該可以猜到為什麼要這樣寫了吧,這就是最基本的 prompt engineering 技巧!讓模型知道什麼角色,說了什麼話。那為什麼不需要再多寫一個 “AI:” 或是 “Assistant:” 呢?因為這些細節 API 都幫我們處理好了,我們只管對話即可。如果你是使用主流模型供應商的 API,我們通常不會真的去做「文字接龍」了。

這邊的 System 就是系統參數的意思,你可以把它想成接在文字接龍最開頭的文字,也就是給模型一個基本的指示,常見的有「使用繁體中文」、「用專業的語氣」、「你是一個xxx」等等。

我們來試著讓 llama3 講繁體中文吧!這邊將 system 的 content 寫成「使用繁體中文回答問題」。之所以使用 ** 來強調是因為大部分的語言模型都有用 Markdown 語法的資料來訓練,這樣可以讓模型知道我們要強調的是繁體中文。

# 檔名為 openai-test.py
# 使用 openai 的 API 來生成文本
import openai
from dotenv import load_dotenv # 載入 dotenv 套件
import os

load_dotenv() # 載入環境變數

input_string = "你好ㄚ可愛的模型"
model_name = "gpt-4o-mini"

# 從環境變數中取得 API 金鑰,並且設定給 openai
openai.api_key = os.getenv("OPENAI_API_KEY") 

client = openai.OpenAI() # 建立 OpenAI 客戶端
completion = client.chat.completions.create(
    model=model_name,
    messages=[
        {
            "role": "system",
            "content": "使用繁體中文回答問題"
        },
        {
            "role": "user", 
            "content": input_string
        }
    ]
)

# 印出回傳的結果
print(completion.choices[0].message.content)

可以發現還是會出現英文,只是機率比昨天低很多了

image3.png

再來看看 gpt-4o-mini 的表現,比 llama3 正經多了,而且沒有出現簡體字!

image4.png

Prompt 有特定格式嗎?

沒有,因為模型的輸出都是機率分佈的結果,我們不能保證每次模型的輸出都是準確的,作為開發者,我們只能不斷的測試和改善 prompt。(聽起來很絕望,要通靈了QQ😱)

但真的只能這樣嗎?其實模型供應商的網站上都會提供一些範例、模板,告訴你如何使用他們家的模型效果最好。這些模板都是根據模型的訓練資料,也就是模型懂的「套路」。網路上也有很多人分享使用 prompt 的測試結果,像是 awesome-chatgpt-prompts

特別是小模型,模板尤其重要,如果你自創 prompt 模板可能會讓他跳出這個他懂的套路,大模型則是因為訓練資料太多,什麼都懂,所以你要通靈可能也是很不錯的做法(?)。那為什麼還要用小模型?因為有時候我們不希望資料外流出去,又希望在穿戴式裝置這種記憶體空間和運算資源相對匱乏的環境中使用 AI。

比較需要注意的是,模型的提示詞或者說上下文長度 (Context Length) 是有限制的,上下文包含「輸入給模型的內容」和「模型輸出的內容」。

OpenAI 的六大策略

雖然我們不能確保模型的輸出結果,但我們還是可以試著提高模型輸出的正確率。接下來介紹 OpenAI 的提示詞六大策略,來讓你的模型更「聽話」。這些策略不僅適用於 gpt 系列模型,也可以應用到其他大型語言模型上 (效果不一定好,需要測試 or 參考模型的說明書),以下就簡單介紹一下,詳細的範例可以參考官方文件

  1. 清晰的指令
  2. 提供參考資料
  3. 將任務拆解成簡單的小任務
  4. 給模型時間「思考」
  5. 使用外部工具
  6. 測試系統變更

這些策略也可以用在平時使用 ChatGPT, Claude 上,不一定要寫程式才可以做到。接下來有範例的 Prompt 都會以大寫的 SYSTEM, USER 來表示程式碼中對應的 key: system, user;用 ASSISTANT 表示模型輸出的結果。

清晰的指令

要知道什麼是清晰的指令,先看看什麼是模糊的指令

  • 範例一

    ❌ 誰是總統?
    ✅ 誰是 2020 年上任的台灣總統?

  • 範例二

    ❌ 摘要這個檔案
    ✅ 使用 Markdown 摘要這份會議紀錄,你可以用 bullet list 列點的方式幫助理解,將每一個講者的演說重點都寫出來,最後寫出該講者針對主題給出的建議。

其實不用模型來看,人類來看也很有感。如果你是工程師,但你的上司卻要你完成一個需求很不明確的任務,做了還會被罵,那豈不是可憐你也可憐老闆。所以,不要這麼懶,多打幾個字就可以讓模型的表現提升超級多,這是最簡單的步驟。

提供參考資料

模型就像人一樣,腦容量是有限的。但如果你今天給我一本字典,我可以回答你所有中文字的部首。一樣的道理,在問問題之前,先給模型閱讀相關參考資料,模型就會根據提供的資料來建構答案。

image5.png

這個例子中,我們的 system 告訴模型要參考三個引號中的資料,而 user 在提問時也會提供參考資料,最後才是問題的文字。這個策略一樣可以搭配策略一,寫越詳細越好。接下來我以〈藍委邀外部顧問共同考察核四,台電:不符保安規定〉這篇關鍵評論網的文章來提問。

SYSTEM: 
使用三個雙引號中提供的文章來回答問題。如果在文章中找不到答案,請回答「我找不到答案。」

USER:
"""(這邊放文章)"""
問題: 有哪學大學的教授參與考察?

回覆正確

image.png

再來我問一個沒有相關的問題:「請問香蕉的成份為何」,結果也符合預期,因為這不是文章的內容。

image.png

將任務拆解成簡單的小任務

複雜任務的錯誤率往往比簡單任務高。就像在軟體工程中,時常將複雜系統分解為一組模組化的組件,對於交給語言模型的任務也是如此。複雜任務通常可以重新定義為一個簡單任務的工作流程,這些簡單任務的輸出可以用來構建後續任務的輸入,有點類似演算法中的分治法 (Divide and conquer)

這個例子把模型當成一個客服人員,模型推論分成兩個階段:第一次推論用來分類問題,第二次推論回答問題,但 Prompt 會有所不同。

  1. 將顧客的問題分類,並且用 JSON 的格式輸出。(延伸閱讀:JSON 格式是什麼?主流程式語言有專門的函式庫在處理 JSON 資料)

    SYSTEM:
    You will be provided with customer service queries. Classify each query into a primary category and a secondary category. Provide your output in json format with the keys: primary and secondary.
    
    Primary categories: Billing, Technical Support, Account Management, or General Inquiry.
    
    Billing secondary categories:
    - Unsubscribe or upgrade
    - Add a payment method
    - Explanation for charge
    - Dispute a charge
    
    Technical Support secondary categories:
    - Troubleshooting
    - Device compatibility
    - Software updates
    
    Account Management secondary categories:
    - Password reset
    - Update personal information
    - Close account
    - Account security
    
    General Inquiry secondary categories:
    - Product information
    - Pricing
    - Feedback
    - Speak to a human
    
    USER:
    I need to get my internet working again.
    

    gpt-4o-mini 輸出結果

    image.png

    (其實這邊的輸出結果不太好,模型輸出了 Markdown 的語法,應該要加上「直接輸出 JSON」 之類的提示,好讓程式不會出錯。)

  2. 程式根據模型的分類結果,使用不同的 Prompt 來做第二階段推論,回覆客人的問題。以下 Prompt 針對 Troubleshooting 類別的問題設計。

    SYSTEM:
    You will be provided with customer service inquiries that require troubleshooting in a technical support context. Help the user by:
    
    - Ask them to check that all cables to/from the router are connected. Note that it is common for cables to come loose over time.
    - If all cables are connected and the issue persists, ask them which router model they are using
    - Now you will advise them how to restart their device:
    -- If the model number is MTD-327J, advise them to push the red button and hold it for 5 seconds, then wait 5 minutes before testing the connection.
    -- If the model number is MTD-327S, advise them to unplug and replug it, then wait 5 minutes before testing the connection.
    - If the customer's issue persists after restarting the device and waiting 5 minutes, connect them to IT support by outputting {"IT support requested"}.
    - If the user starts asking questions that are unrelated to this topic then confirm if they would like to end the current chat about troubleshooting and classify their request according to the following scheme:
    
    USER:
    I need to get my internet working again.
    

    image.png

來說說為什麼上面的例子是一個好的 Prompt,因為它使用了以下技巧

  • 寫出明確的指令:在第一階段中,我們很清楚告訴模型 JSON 的格式是有兩個 Key 的。
  • 提供參考資料:提供一些範例給它參考,避免模型輸出複雜的答案
  • 問題分類

還有個小技巧就是讓模型「反省」你可以試著在 ChatGPT 生成答案後,請他檢查自己的答案,也可以提高最終回覆的正確率喔。

給模型時間「思考」

這邊的時間不是真正的時間,更像是「步驟」的概念,不必讓模型的答案一次到位,只要最後的答案正確即可。這邊的步驟可以是一次推論的內容長度加長,也可以是推論的次數增加。

以下是一個讓模型幫學生改作業的例子

在以前我們可能會寫出這樣的 Prompt,直接讓模型輸出正確或是錯誤,但很明顯學生錯了,第三點應該是 100,000 + 10x 才對,模型卻說他對,太糟了。

SYSTEM:
Determine if the student's solution is correct or not.

USER:
Problem Statement: I'm building a solar power installation and I need help working out the financials.
- Land costs $100 / square foot
- I can buy solar panels for $250 / square foot
- I negotiated a contract for maintenance that will cost me a flat $100k per year, and an additional $10 / square foot
What is the total cost for the first year of operations as a function of the number of square feet.

Student's Solution: Let x be the size of the installation in square feet.
1. Land cost: 100x
2. Solar panel cost: 250x
3. Maintenance cost: 100,000 + 100x
Total cost: 100x + 250x + 100,000 + 100x = 450x + 100,000

ASSISTANT:
The student's solution is correct.

以下是修改後的 Prompt,我們只修改 SYSTEM 指令,讓模型先輸出自己的答案,再和學生的答案比較

SYSTEM:
First work out your own solution to the problem. Then compare your solution to the student's solution and evaluate if the student's solution is correct or not. Don't decide if the student's solution is correct until you have done the problem yourself.

如此一來,模型就能發現自己得答案和學生的答案不同,進而輸出學生的答案是錯的

ASSISTANT:
Let x be the size of the installation in square feet.
1. Land cost: 100x
2. Solar panel cost: 250x
3. Maintenance cost: 100,000 + 10x
Total cost: 100x + 250x + 100,000 + 10x = 360x + 100,000

The student's solution is incorrect. They made an error in the maintenance cost calculation, using 100x instead of 10x. The correct total cost for the first year of operations is 360x + 100,000.

上面這個例子還有一些可以改進的地方:今天的問題是比較簡單的數學題,如果是更複雜的問題應該要用 Chain Of Thought 技巧 (論文連結白話說明) 來解決會更好。

使用外部工具

模型終究是一個文字接龍工具,能力有限,我們可以通過使用外部工具來增強模型的能力。例如:檢索增強生成 (Retrieval augmented generation, RAG) 系統,可以讓模型有更精確的參考資料來生成回應;或者用程式碼來呼叫計算機,而不是讓模型做數學運算。

image.png

(圖片來源: https://aws.amazon.com/tw/what-is/retrieval-augmented-generation/)

更多使用工具例子會在之後的文章中介紹喔!

測試系統變更

改進模型能力最快的方法就是去修改 Prompt,我們要怎麼知道修改後的 Prompt 真的有改善系統,而不是對特定任務有提升而已呢?測試就很重要了。OpenAI 有一套開源工具 https://github.com/openai/evals 就是用來評估 LLM 系統的。

關於模型好壞的評估,又是一個大議題了,這邊就不多介紹了 (也可能暫時用不到),以後再說吧。

更多技巧

更多 Prompt Engineering 技巧可以參考 Prompt Engineering Guide 這個網站,我不打算細講這塊,如果有興趣再查找相關論文即可,不過些技巧幾乎都有對應到這六大原則,有了基本概念再去看很快就可以理解了。或者去看李宏毅老師的生成式 AI 講座也可以學到很多相關知識。

這些是我自己覺得有趣的技巧,很值得研究一番

  • CoT (Chain of Thought): 讓模型一步一步思考
  • ReAct (Reason + Act): 使用外部工具讓回答更可靠
  • In context learning: 在上下文中學習 (給範例)

關於 Prompt 的研究真的很多,可以參考整理好的 LLM 各種技巧 | Prompt Engineering 大總結 | 指南 超級多的

Prompt Engineering 的未來

李宏毅老師在生成式 AI 講座中提到,這些 Prompt Engineering 技巧會變得越來越不重要,因為大型語言模型變得更「聰明」了,資料量已經遠比過去的模型還要大。隨著 LLM 理解的知識變多、能接受的上下文更大,能理解更多樣的自然語言,就更可能減少人工設計提示詞的需求了。

image.png

(影片擷取自 【生成式AI導論 2024】第6講)

不過在可以接受無限上下文的模型出現之前,還是好好通靈吧XD

明天會來實作模型的記憶功能,期待一下吧~


上一篇
【Day3】第一次使用 LLM API:用 OpenAI API 和 LLM 說聲嗨 (還有免費的 Groq API)
下一篇
【Day5】初探 AI 記憶功能:讓你的 LLM 不再「失憶」
系列文
Python 新手的 AI 之旅:從零開始打造屬於你的 AI / LLM 應用12
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言